﻿using System.Collections.Generic;
using System.Composition;
using System.Composition.Hosting;
using System.Linq;
using System.Events;
using System.Reflection;
using System.Services;
using System.Threading.Tasks;
using Microsoft.Practices.Prism.Regions;
using Windows.ApplicationModel;
using Windows.ApplicationModel.Activation;
using Windows.Storage;
using Windows.UI.Popups;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;

namespace System.Applications
{
    public class HDKApplication : global::Windows.UI.Xaml.Application, IDisposable
    {
        public CompositionHost Container { get; private set; }

        [Import]
        public IRegionManager RegionManager { get; set; }

        [Import]
        public IEventPublisher EventAggregator { get; set; }

        [Import]
        public ISessionStateManager SessionStateManager { get; set; }

        [Import]
        public ILoggerService Logger { get; set; }


        /// <summary>
        /// Gets or sets the initialized.
        /// </summary>
        /// <value>The initialized.</value>
        protected List<Lazy<IService, ExportServiceAttribute>> Initialized { get; set; }


        /// <summary>
        /// Gets or sets the services.
        /// </summary>
        /// <value>The services.</value>
        [ImportMany]
        public IList<Lazy<IService, ExportServiceAttribute>> Services { get; set; }

        /// <summary>
        /// Initializes the singleton application object.  This is the first line of authored code
        /// executed, and as such is the logical equivalent of main() or WinMain().
        /// </summary>
        public HDKApplication()
        {
            this.UnhandledException += OnUnhandledException;


            this.Suspending += (s, ea) => OnSuspending(ea);
            this.Resuming += (s, ea) => OnResuming(ea);

            var folder = global::Windows.ApplicationModel.Package.Current.InstalledLocation;
            InitializeContainer(folder);
        }

        protected virtual async void OnSuspending(SuspendingEventArgs ea)
        {
            var deferral = ea.SuspendingOperation.GetDeferral();
            await SessionStateManager.SaveAsync();
            deferral.Complete();
        }

        protected virtual async void OnResuming(object ea)
        {
            await SessionStateManager.RestoreAsync();
        }

        private async Task<IEnumerable<Assembly>> GetAssemblyListAsync(StorageFolder folder)
        {
            List<Assembly> assemblies = new List<Assembly>();
            foreach (StorageFile file in await folder.GetFilesAsync())
            {
                if (file.FileType == ".dll" || file.FileType == ".exe")
                {
                    AssemblyName name = new AssemblyName() { Name = file.DisplayName };
                    Assembly asm = Assembly.Load(name);
                    assemblies.Add(asm);
                }
            }

            return assemblies;
        }
        protected virtual void InitializeContainer(StorageFolder appFolder)
        {
            try
            {
                var configuration = new ContainerConfiguration();
                var Result = GetAssemblyListAsync(appFolder).Result;

                foreach (var asm in Result)
                    configuration.WithAssembly(asm);

                Container = configuration.CreateContainer();

                Container.SatisfyImports(this);
            }
            catch (Exception exc)
            {
                Logger.Log(LogSeverity.Critical, this.GetType().ToString(), exc);
                throw exc;
            }
        }
        protected virtual void OnUnhandledException(object sender, UnhandledExceptionEventArgs e)
        {
            Logger.Log(LogSeverity.Error, sender.GetType().ToString(), e.Exception);
        }
        protected override void OnCachedFileUpdaterActivated(CachedFileUpdaterActivatedEventArgs args)
        {
            base.OnCachedFileUpdaterActivated(args);
        }
        protected override void OnFileActivated(FileActivatedEventArgs args)
        {
            base.OnFileActivated(args);
        }
        protected override void OnFileOpenPickerActivated(FileOpenPickerActivatedEventArgs args)
        {
            base.OnFileOpenPickerActivated(args);
        }
        protected override void OnFileSavePickerActivated(FileSavePickerActivatedEventArgs args)
        {
            base.OnFileSavePickerActivated(args);
        }
        protected override void OnSearchActivated(SearchActivatedEventArgs args)
        {
            base.OnSearchActivated(args);
        }
        protected override void OnShareTargetActivated(ShareTargetActivatedEventArgs args)
        {
            base.OnShareTargetActivated(args);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="args"></param>
        protected override async void OnActivated(IActivatedEventArgs args)
        {
            base.OnActivated(args);
            await SessionStateManager.RestoreAsync();
        }

        /// <summary>
        /// Invoked when the application is launched normally by the end user.  Other entry points
        /// will be used when the application is launched to open a specific file, to display
        /// search results, and so forth.
        /// </summary>
        /// <param name="args">Details about the launch request and process.</param>
        protected override async void OnLaunched(LaunchActivatedEventArgs args)
        {
            base.OnLaunched(args);
            await SessionStateManager.RestoreAsync();

            var shell = CreateShell();
            Container.SatisfyImports(shell);

            Window.Current.Content = shell;
            Window.Current.Activate();
        }

        protected virtual UIElement CreateShell()
        {
            var shell = new Frame();
            global::Microsoft.Practices.Prism.Regions.RegionManager.SetRegionManager(shell, this.RegionManager);
            global::Microsoft.Practices.Prism.Regions.RegionManager.SetRegionName(shell, "HDKShell");
            return shell;
        }

        protected virtual UIElement CreateSettingsPane()
        {
            throw new NotImplementedException("Settings page has to be context view model aware");
            /*
		/// <summary>
		/// Invoked when this page is about to be displayed in a Frame.
		/// </summary>
		/// <param name="e">Event data that describes how this page was reached.  The Parameter
		/// property is typically used to configure the page.</param>
		protected override void OnNavigatedTo(NavigationEventArgs e)
		{
			settingswidth.SelectedIndex = 0;
			SettingsPane.GetForCurrentView().CommandsRequested += BlankPage_CommandsRequested;
		}


		protected override void OnNavigatingFrom(NavigatingCancelEventArgs e)
		{
			SettingsPane.GetForCurrentView().CommandsRequested -= BlankPage_CommandsRequested;
			base.OnNavigatingFrom(e);
		}


		private void BlankPage_CommandsRequested(SettingsPane sender, SettingsPaneCommandsRequestedEventArgs args)
		{
			SettingsCommand cmd = new SettingsCommand("sample", "Sample Custom Setting", (x) =>
			{
				SettingsFlyout settings = new SettingsFlyout();
				settings.FlyoutWidth = (Callisto.Controls.SettingsFlyout.SettingsFlyoutWidth)Enum.Parse(typeof(Callisto.Controls.SettingsFlyout.SettingsFlyoutWidth), settingswidth.SelectionBoxItem.ToString());
				//settings.HeaderBrush = new SolidColorBrush(Colors.Orange);
				settings.HeaderText = "Foo Bar Setting";


                BitmapImage bmp = new BitmapImage(new Uri("ms-appx:///Assets/SmallLogo.png"));


                settings.SmallLogoImageSource = bmp;


				ToggleSwitch ts = new ToggleSwitch();
				ts.Header = "Download updates automatically";


				settings.Content = ts;


				settings.IsOpen = true;


				ObjectTracker.Track(settings);
			});


			args.Request.ApplicationCommands.Add(cmd);
		}


		private void ShowSettings(object sender, RoutedEventArgs e)
		{
			SettingsPane.Show();
		}
            */
        }


        /// <summary>
        /// Called when a part's imports have been satisfied and it is safe to use.
        /// </summary>
        [OnImportsSatisfied]
        public void OnImportsSatisfied()
        {
            if (Services == null)
                return;

            this.Initialized = new List<Lazy<IService, ExportServiceAttribute>>();

            Logger.Log(LogSeverity.Information, "LifetimeService", string.Format("Discovered Services: {0}", Services.Count()));


            // buffer for services that raises exception during initialization
            List<Lazy<IService, ExportServiceAttribute>> errors = new List<Lazy<IService, ExportServiceAttribute>>();

            // compose and call StartService 
            IEnumerable<Lazy<IService, ExportServiceAttribute>> notstartedservices = this.Services.Except(this.Initialized).OrderBy((svc) => svc.Metadata.SequenceNumber);
            foreach (Lazy<IService, ExportServiceAttribute> service in notstartedservices)
            {
                try
                {
                    Logger.Log(LogSeverity.Verbose, "LifetimeService", string.Format("Starting [{0}]", service.Metadata.ServiceName));
                    service.Value.StartService();
                }
                catch (Exception exc)
                {
                    Logger.Log(LogSeverity.Error, "LifetimeService", exc);
                    errors.Add(service);
                }
            }

            //// register ad Initialized to avoid duplicated initialization in case of recomposition
            //foreach (Lazy<IService, IServiceMetadata> service in this.Services.Except(this.Initialized).Except(errors))
            //    this.Initialized.Add(service);

            //// remove services that raised exceptions
            //foreach (Lazy<IService, IServiceMetadata> service in errors)
            //    this.Services.Remove(service);
        }
        public void Dispose()
        {
            this.Initialized.Where((lo) => lo.IsValueCreated).ForEach((svc) => svc.Value.Dispose());
        }
        public object GetService(Type serviceType)
        {
            return GetServiceByType(serviceType);
        }
        public T GetService<T>()
        {
            return (T)GetServiceByType(typeof(T));
        }
        public IService this[Type serviceType]
        {
            get { return GetServiceByType(serviceType); }
        }
        public IService this[string Name]
        {
            get { return GetServiceByName(Name); }
        }
        private IService GetServiceByName(string name)
        {
            Lazy<IService, ExportServiceAttribute> foundout = Services.Where((lo) => lo.Metadata.ServiceName.Equals(name)).FirstOrDefault();
            if (foundout != null)
                return foundout.Value;

            return default(IService);
        }
        private IService GetServiceByType(Type ServiceType)
        {
            Lazy<IService, ExportServiceAttribute> foundout = Services.Where((lo) => lo.Metadata.ServiceType.Equals(ServiceType)).FirstOrDefault();
            if (foundout != null)
                return foundout.Value;

            return default(IService);
        }
    }
}
